#pragma once

#include "Svn.h"
#include "SvnInfo.h"
#include "SvnAction.h"
#include "svn_pools.h"
#include "svn_client.h"
#include "svn_error.h"
#include "svn_repos.h"
#include "svn_time.h"
#include "vcclr.h"
#include "svn_sorts.h"
#include "SvnEntry.h"
#include "svn_fs.h"
#include "SvnRevOrTxn.h"
#include "SvnNode.h"
#include "SvnLookInfo.h"

using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Collections::Generic;
using namespace System::IO;
using namespace System::Diagnostics;

svn_error_t* GetLogMessage(
	const char **log_msg,
	const char **tmp_file,
	const apr_array_header_t *commit_items,
	void *baton,
	apr_pool_t *pool
);

svn_error_t* InfoReceiver(
	void *baton,
	const char *path,
	const svn_info_t *info,
	apr_pool_t *pool
);

void Notify(
	void *baton,
	const svn_wc_notify_t *notify,
	apr_pool_t *pool
);

svn_error_t* AuthUserPassword(
	svn_auth_cred_simple_t **cred_p,
	void *baton,
	const char *realm,
	const char *username, 
	svn_boolean_t may_save,
	apr_pool_t *pool
);

svn_error_t* AuthUser(
	svn_auth_cred_username_t **cred_p, 
	void *baton,
	const char *realm, 
	svn_boolean_t may_save, 
	apr_pool_t *pool
);

svn_error_t* AuthSslTrust(
	svn_auth_cred_ssl_server_trust_t **cred_p,
	void *baton,
	const char *realm,
	apr_uint32_t failures,
	const svn_auth_ssl_server_cert_info_t *cert_info,
	svn_boolean_t may_save,
	apr_pool_t *pool
);

svn_error_t* AuthSsl(
	svn_auth_cred_ssl_client_cert_t **cred_p,
	void *baton, 
	const char *realm, 
	svn_boolean_t may_save,
	apr_pool_t *pool
);

svn_error_t* AuthSslPassword(
	svn_auth_cred_ssl_client_cert_pw_t **cred_p,
	void *baton, 
	const char *realm, 
	svn_boolean_t may_save,
	apr_pool_t *pool
);

namespace IronSvn {

	ref class SvnClient;

	[Serializable]
	private ref class SvnClientWrapper
	{
	public:
		SvnClientWrapper(SvnClient^ client);
		~SvnClientWrapper();
		property SvnClient^ Client { SvnClient^ get(); };
	private:
		SvnClient^ m_client;
	};

	[Serializable]
	public ref class SvnResult
	{
	public:
		SvnResult() {Actions = gcnew List<IronSvn::SvnAction^>();};
		SvnResult(const SvnResult^ copy) {this->Result = copy->Result; this->Actions = copy->Actions;};

		virtual String^ ToString() override {
			String^ result = Result.ToString();
			if (Actions != nullptr)
			{
				for (int i = 0; i < Actions->Count; i++)
				{
					result += "; " + Actions[i]->ToString();
				}
			}
			return result;
		};

		List<SvnAction^>^ Actions;
		long Result;
	};

	[Serializable]
	public ref class SvnClient : IDisposable
	{
	public:
		SvnClient();
		SvnClient(String^ userName, String^ password);
		~SvnClient();

		SvnResult^ Checkout(String^ url, String^ checkoutPath);
		SvnResult^ Checkout(String^ url, String^ checkoutPath, long revision);
		SvnResult^ Update(String^ path);
		SvnResult^ Update(String^ path, long revision);

		// Returns -1 in SvnResult::Result there is nothing to commit
		SvnResult^ Commit(String^ path, String^ message);
		SvnResult^ Add(String^ path);
		SvnResult^ MakeDirectory(String^ dir);
		SvnResult^ MakeDirectory(String^ dir, String^ message);
		SvnResult^ Delete(String^ path);
		SvnResult^ Delete(String^ path, bool force);
		SvnResult^ Move(String^ src, String^ dest);
		SvnResult^ Move(String^ src, String^ dest, String^ message);
		List<SvnEntry^>^ Ls(String^ path);
		SvnInfo^ Info(String^ pathOrUrl);
		List<SvnInfo^>^ Info(String^ pathOrUrl, bool recursive);
		bool HasTarget(String^ path);
		void Cleanup(String^ workingCopy);
		void Revert(String^ path);
		List<SvnNode^>^ Changed(String^ repo);
		List<SvnNode^>^ Changed(String^ repo, long revision);
		List<SvnNode^>^ Changed(String^ repo, String^ transaction);
		List<SvnNode^>^ Changed(String^ repo, SvnRevOrTxn^ target);
		SvnLookInfo^ GetAuthor(String^ repo);
		SvnLookInfo^ GetAuthor(String^ repo, long revision);
		SvnLookInfo^ GetAuthor(String^ repo, String^ transaction);
		SvnLookInfo^ GetAuthor(String^ repo, SvnRevOrTxn^ target);
		SvnLookInfo^ GetLog(String^ repo);
		SvnLookInfo^ GetLog(String^ repo, long revision);
		SvnLookInfo^ GetLog(String^ repo, String^ transaction);
		SvnLookInfo^ GetLog(String^ repo, SvnRevOrTxn^ target);
		SvnLookInfo^ GetDate(String^ repo);
		SvnLookInfo^ GetDate(String^ repo, long revision);
		SvnLookInfo^ GetDate(String^ repo, String^ transaction);
		SvnLookInfo^ GetDate(String^ repo, SvnRevOrTxn^ target);
		SvnLookInfo^ GetInfo(String^ repo);
		SvnLookInfo^ GetInfo(String^ repo, long revision);
		SvnLookInfo^ GetInfo(String^ repo, String^ transaction);
		SvnLookInfo^ GetInfo(String^ repo, SvnRevOrTxn^ target);

		void CreateRepository(String^ path);
		void DeleteRepository(String^ path);

		property String^ UserName { String^ get(); void set(String^ userName); };
		property String^ Password { String^ get(); void set(String^ password); };
	internal:
		void AddInternal(SvnResult^ result, String^ path);
		property const char* UserNameChars { const char* get(); };
		property const char* PasswordChars { const char* get(); };
	private:
		apr_pool_t* m_pool;
		svn_client_ctx_t* m_ctx;
		String^ m_userName;
		String^ m_password;
		SvnClientWrapper^ m_thisWrapper;
		gcroot<SvnClientWrapper^>* m_wrapper;
		const char* m_userChars;
		const char* m_passwordChars;
	
		void Initialize(String^ userName, String^ password);
		void ProcessTree(svn_repos_node_t *node, String^ path, String^ relativePath, List<SvnNode^>^ result);
		const char* GetProperty(String^ repo, SvnRevOrTxn^ target, const char* prop, long* revision);
	};
}
